import type { ReactNode } from 'react';
import React, {
  forwardRef,
  useRef,
  useCallback,
  useState,
  useEffect,
  useMemo,
  useLayoutEffect,
  useImperativeHandle,
} from 'react';
import { difference as _difference, isEqual as _isEqual, unionBy, isEmpty } from 'lodash';
import type { SearchFormSchema, SearchFormRef } from '@/components/searchform';
import { useGrid } from '@/utils/hooks';
import TransferItem from './TransferItem';
import Operate from './Operate';
import { getRowKey } from './utils';
import styles from './index.less';

export type Direction = 'left' | 'right' | string;

export type Pageinfo = {
  beginIndex: number; // 分页起始位置
  pageSize: number; // 分页大小
};

export type SizeType = 'searchForm' | 'beforeSearch' | 'afterSearch' | 'afterGrid';

// 两边table内的私有配置
export interface GridTransferInnerProps {
  title: string; // 显示的标题
  loading?: boolean; // table的loading状态
  pagination?: {
    total: number; // 数据总条数
  } & Pageinfo;
  extraSearchProps?: any; // 额外的SearchForm配置
  extraGridProps?: any; // 额外的table配置
  beforeSearch?: ReactNode; // 在SearchForm前渲染
  afterSearch?: ReactNode; // 在SearchForm后，Grid前渲染
  afterGrid?: ReactNode; // 在Grid后渲染
  filterKey?: string; // 筛选字段的名称
}

export type SetFilterKeyValue = (direction: Direction, filterKeyValue: string) => void;
export type ResetSearchForm = (type: 'left' | 'right' | 'both') => void;
export type ResetSelectedRows = (type: 'left' | 'right' | 'both') => void;

export type GridTransferRef = {
  setFilterKeyValue: SetFilterKeyValue; // 设置过滤筛选值
  resetSearchForm: ResetSearchForm; // 重置searchForm的值
  resetSelectedRows: ResetSelectedRows; // 重置grid选中项
};

export type RowKey = (record: any) => string;

export type onChangeParams = {
  clearSelection: () => void; // 清除被穿梭数据的选中状态
  handleFail: () => void; // 处理穿梭失败，目前就要解决拖拽穿梭后，业务上阻止穿梭，但grid上显示已传输完成
};

export type OnSearch = (direction: Direction, params: any, pageInfo: PageInfo) => void;
export type OnChange = (targetKeys: string[], direction: Direction, moveKeys: string[], params: onChangeParams) => void;
export type OnSortChange = (sortKeys: string[], direaction: Direction) => void;

export interface BaseGridTransferProps {
  transKey: string; // 该组件的唯一标识，SearchForm组件和Table组件内容会用到
  rowKey: string | RowKey; // 每行数据的唯一标识
  schema?: SearchFormSchema; // SearchForm的schena配置,传入该属性则表示会显示SearchForm组件
  columns?: any[]; // table的列
  left: GridTransferInnerProps; // 左边部分的配置
  right: GridTransferInnerProps; // 右边部分的配置
  dataSource: any[]; // table的数据源（包含两边table所有的数据）
  targetKeys: string[]; // 右边选中的数据keys，由rowKey来指定对应数据项
  onSearch?: OnSearch; // 触发查询的回调,分页改变或者点击查询按钮会触发
  onChange: OnChange; // 穿梭数据的回调
  filterKey?: string; // 通过这段来过滤的字段名，如果不传，默认匹配所有字段
  ref: GridTransferRef;
  onSortChange?: OnSortChange; // 穿梭两边各自拖拽顺序变化回调
  disabled?: boolean; // 是否禁用穿梭功能
  selectionClear?: boolean; // 是否组件内部自动清除选中项，如果业务层有判断穿梭不成功的场景，需设置为false
  defaultExportParams?: any; //导出参数,同 grid defaultExportParams属性
  onReady?: (params: any) => void; //两边grid onReady
}

export interface GridTransferProps extends BaseGridTransferProps {
  height: string | number; // 整个组件的高度
  width?: string | number; // 整个组件的宽度
}

export type GridTransferAllProps = keyof GridTransferProps;

/**
 * GridTransfer 表格穿梭框组件
 */
export default forwardRef((props: GridTransferProps, refDom: any) => {
  const {
    transKey,
    rowKey,
    schema,
    columns,
    left,
    right,
    dataSource,
    targetKeys,
    height,
    width = '100%',
    onSearch,
    onChange,
    filterKey,
    onSortChange,
    disabled = false,
    selectionClear = true,
    defaultExportParams,
    onReady,
  } = props;

  const currentRef = useRef(null);
  const ref = refDom || currentRef;
  const leftSearchFormRef = useRef({} as SearchFormRef);
  const rightSearchFormRef = useRef({} as SearchFormRef);
  const [leftFilterKeyValue, setLeftFilterKeyValue] = useState('');
  const [rightFilterKeyValue, setRightFilterKeyValue] = useState('');
  const leftSeletAllFunc = useRef<Function>(() => {});
  const rightSeletAllFunc = useRef<Function>(() => {});
  const wrapperRef = useRef({} as HTMLDivElement);
  const dragStateRef = useRef(false);
  const leftRowDragZonesRef = useRef<any[]>([]);
  const [leftReadyTimestamp, setLeftReadyTimestamp] = useState<number>();
  const rightRowDragZonesRef = useRef<any[]>([]);
  const [rightReadyTimestamp, setRightReadyTimestamp] = useState<number>();
  const [leftSelectedRows, setLeftSelectedRows] = useState<any[]>([]);
  const [rightSelectedRows, setRightSelectedRows] = useState<any[]>([]);
  const [timestamp, setTimestamp] = useState(Date.now());

  const [leftDataSource, rightDataSource] = useMemo(() => {
    const tempDataSource = unionBy(dataSource, rowKey);
    const Arr = tempDataSource.reduce(
      (res, item, index) => {
        const key = getRowKey(item, rowKey);
        if (targetKeys.includes(key)) {
          res[1][key] = item;
        } else {
          res[0].push(item);
        }
        if (index === tempDataSource.length - 1) {
          res[1] = targetKeys.map((key) => res[1][key]);
        }
        return res;
      },
      [[], {}],
    );
    if (isEmpty(Arr[1])) {
      Arr[1] = [];
    }
    return Arr;
  }, [dataSource, targetKeys, rowKey]);

  const {
    onGridReady: onLeftGridReady,
    gridApiRef: leftGridApiRef,
    gridManagerRef: leftGridManagerRef,
  } = useGrid({
    onReadyCallback: (api: any) => {
      setLeftReadyTimestamp(Date.now());
    },
  });
  const {
    onGridReady: onRightGridReady,
    gridApiRef: rightGridApiRef,
    gridManagerRef: rightGridManagerRef,
  } = useGrid({
    onReadyCallback: () => {
      setRightReadyTimestamp(Date.now());
    },
  });

  const clearSelectedRows = useCallback((direction: Direction) => {
    let func = () => {};
    if (direction === 'left') {
      func = () => setLeftSelectedRows([]);
    } else {
      func = () => setRightSelectedRows([]);
    }
    setTimeout(func, 16 * 20);
  }, []);

  const setFilterKeyValue = useCallback((direction: Direction, keyValue: string) => {
    let setFunc = setLeftFilterKeyValue;
    let selectAllFunc = leftSeletAllFunc.current;
    if (direction === 'right') {
      setFunc = setRightFilterKeyValue;
      selectAllFunc = rightSeletAllFunc.current;
    }
    selectAllFunc([], false);
    setFunc(keyValue);
  }, []);

  const onChange_ = useCallback(
    (resKeys: string[], direction: Direction, moveKeys: string[]) => {
      if (selectionClear) {
        clearSelectedRows(direction === 'left' ? 'right' : 'left');
      }
      const params = {
        clearSelection: clearSelectedRows.bind(null, direction === 'left' ? 'right' : 'left'),
        handleFail: () => setTimestamp(Date.now()),
      };
      if (direction === 'right') {
        onChange && onChange([...targetKeys, ...moveKeys], direction, moveKeys, params);
      } else {
        onChange && onChange(resKeys, direction, moveKeys, params);
      }
    },
    [onChange, targetKeys, selectionClear],
  );

  function changeTextSelection(selectable: boolean) {
    leftGridApiRef.current.setEnableCellTextSelection(selectable);
    rightGridApiRef.current.setEnableCellTextSelection(selectable);
    document.onselectstart = () => selectable;
    window.getSelection()?.empty();
  }

  // 右边区域拖拽结束事件
  const onRightRowDrag = useCallback(
    (e: any) => {
      const { api, nodes = [], overIndex, y, overNode } = e;

      // 右边现有keys
      const showKeys = api.getModel().rowsToDisplay.map((item: any) => {
        const { data } = item;
        return getRowKey(data, rowKey);
      });

      dragStateRef.current = false;
      changeTextSelection(true);

      // 没变化
      if (_isEqual(targetKeys, showKeys)) {
        return;
      }

      // 被拖拽的key列表
      const dragKeys = nodes.map((node) => getRowKey(node.data, rowKey));

      // 从左往右拖拽
      if (showKeys.length !== targetKeys.length) {
        let newKeys = [];
        if (overIndex === -1) {
          newKeys = [...targetKeys, ...dragKeys];
        } else {
          newKeys = [...targetKeys];
          const newOverIndex = y - overNode.rowHeight * overIndex > overNode.rowHeight / 2 ? overIndex + 1 : overIndex;
          newKeys.splice(newOverIndex, 0, ...dragKeys);
        }
        if (selectionClear) {
          clearSelectedRows('left');
        }
        const params = {
          clearSelection: () => {
            clearSelectedRows('left');
            setTimestamp(Date.now());
          },
          handleFail: () => {
            setTimestamp(Date.now());
          },
        };
        onChange && onChange(newKeys, 'right', dragKeys, params);
      } else {
        onSortChange && onSortChange(showKeys, 'right');
      }
    },
    [onSortChange, rowKey, targetKeys, onChange],
  );

  // 左边区域拖拽结束事件
  const onLeftRowDrag = useCallback(
    (e: any) => {
      const { api } = e;
      // 左边现有数据
      const newKeys: string[] = api.getModel().rowsToDisplay.map((item: any) => {
        const { data } = item;
        return getRowKey(data, rowKey);
      });
      // 左边之前的数据
      const oldKeys: string[] = [];
      dataSource.forEach((item) => {
        const key = getRowKey(item, rowKey);
        if (!targetKeys.includes(key)) {
          oldKeys.push(key);
        }
      });
      dragStateRef.current = false;
      changeTextSelection(true);
      // 没变化
      if (_isEqual(newKeys, oldKeys)) return;

      // 现在新的targetKeys
      const newTargetkeys = _difference(targetKeys, newKeys);
      // 穿梭的keys
      const transferKeys = _difference(newKeys, oldKeys);

      // 从右往左拖拽
      if (transferKeys.length > 0) {
        if (selectionClear) {
          clearSelectedRows('right');
        }
        const params = {
          clearSelection: () => {
            clearSelectedRows('right');
          },
          handleFail: () => {
            setTimestamp(Date.now());
          },
        };
        onChange && onChange(newTargetkeys, 'left', transferKeys, params);
      } else {
        onSortChange && onSortChange(newKeys, 'left');
      }
      leftGridApiRef.current.setSuppressRowDrag(false);
    },
    [onSortChange, rowKey, targetKeys, dataSource, onChange],
  );

  // 开始进入拖拽区
  const onRowDragEnter = useCallback((direction: Direction, params: any) => {
    dragStateRef.current = true;
    changeTextSelection(false);
  }, []);

  const onClick = useCallback(function (this: HTMLDivElement, e: MouseEvent): any {
    try {
      const { target } = e;
      if (target.classList.value.includes('ag') && dragStateRef.current) {
        e.stopPropagation();
        e.preventDefault();
      }
    } catch (error) {}
  }, []);

  const addRowDropZone = useCallback(() => {
    let leftDropZone = null;
    let rightDropZone = null;

    leftDropZone = leftGridApiRef.current?.getRowDropZoneParams();
    rightDropZone = rightGridApiRef.current?.getRowDropZoneParams();

    if (leftDropZone) {
      try {
        rightRowDragZonesRef.current.forEach(rightGridApiRef.current?.removeRowDropZone);
      } catch (error) {}
      rightGridApiRef.current?.addRowDropZone(leftDropZone);
      rightRowDragZonesRef.current.push(leftDropZone);
    }
    if (rightDropZone) {
      try {
        leftRowDragZonesRef.current.forEach(leftGridApiRef.current?.removeRowDropZone);
      } catch (error) {}
      leftGridApiRef.current?.addRowDropZone(rightDropZone);
      leftRowDragZonesRef.current.push(rightDropZone);
    }
  }, []);

  const resetSearchForm: ResetSearchForm = useCallback((type = 'both') => {
    const { reset: leftReset } = leftSearchFormRef.current;
    const { reset: rightReset } = rightSearchFormRef.current;
    switch (type) {
      case 'both':
        leftReset && leftReset();
        rightReset && rightReset();
        break;
      case 'left':
        leftReset && leftReset();
        break;
      case 'right':
        rightReset && rightReset();
        break;
    }
  }, []);

  const resetSelectedRows = useCallback((type = 'both') => {
    switch (type) {
      case 'both':
        setLeftSelectedRows([]);
        setRightSelectedRows([]);
        break;
      case 'left':
        setLeftSelectedRows([]);
        break;
      case 'right':
        setRightSelectedRows([]);
        break;
    }
  }, []);

  useEffect(() => {
    ref.current = {
      setFilterKeyValue,
      resetSearchForm,
      resetSelectedRows,
    };
  }, []);

  useEffect(() => {
    if (leftReadyTimestamp && rightReadyTimestamp) {
      // 两边grid互相设置拖拽区域
      addRowDropZone();
    }
  }, [leftReadyTimestamp, rightReadyTimestamp]);

  useEffect(() => {
    wrapperRef.current.addEventListener('click', onClick, true);
    return () => {
      wrapperRef.current.removeEventListener('click', onClick);
    };
  }, []);

  useLayoutEffect(() => {
    onReady &&
      onReady({
        leftGridApiRef,
        rightGridApiRef,
        leftGridManagerRef,
        rightGridManagerRef,
      });
  }, []);

  useImperativeHandle(ref, () => ({
    clearSelectedRows,
  }));
  return (
    <div ref={wrapperRef} className={styles.wrapper} style={{ height, width }}>
      <TransferItem
        transKey={transKey}
        direction="left"
        height={height}
        schema={schema}
        disabled={disabled}
        columns={columns}
        innerProps={left}
        filterKey={filterKey}
        filterValue={leftFilterKeyValue}
        rowKey={rowKey}
        selectedRows={leftSelectedRows}
        dataSource={leftDataSource}
        searchRef={leftSearchFormRef}
        defaultExportParams={defaultExportParams}
        onSelect={setLeftSelectedRows}
        onSearch={onSearch}
        onGridReady={onLeftGridReady}
        onRowDragEnter={onRowDragEnter}
        onRowDrag={onLeftRowDrag}
      />
      <Operate
        leftSelectedRows={leftSelectedRows}
        rightSelectedRows={rightSelectedRows}
        rowKey={rowKey}
        targetKeys={targetKeys}
        disabled={disabled}
        onChange={onChange_}
      />
      <TransferItem
        transKey={transKey}
        direction="right"
        height={height}
        schema={schema}
        disabled={disabled}
        columns={columns}
        innerProps={right}
        filterKey={filterKey}
        filterValue={rightFilterKeyValue}
        rowKey={rowKey}
        dataSource={rightDataSource}
        searchRef={rightSearchFormRef}
        selectedRows={rightSelectedRows}
        defaultExportParams={defaultExportParams}
        onSelect={setRightSelectedRows}
        onSearch={onSearch}
        onGridReady={onRightGridReady}
        onRowDragEnter={onRowDragEnter}
        onRowDrag={onRightRowDrag}
      />
    </div>
  );
});
